#define _GNU_SOURCE
#include "app_device.h"
#include <fcntl.h>
#include "app_buffer.h"
#include "app_pool.h"
#include "app_mqtt.h"
#include "log/log.h"
#include <unistd.h>
#include "app_message.h"
#include <stdlib.h>
#include "app_common.h"

#define BUFFER_SIZE 1024
#define POOL_SIZE 5
#define INTERVAL_TIME_MS 200  // 两次写入的最小时间间隔

static Device *device = NULL;

Device *app_device_init(char *filename) {
    // 保证只产生一个设备
    if (device) {
        return device;
    }
    // 申请设备内存
    device = (Device *)malloc(sizeof(Device));
    // 初始化其属性
    device->filename = filename;
    device->fd = open(filename, O_RDWR);
    device->up_buffer = app_buffer_init(BUFFER_SIZE);
    device->down_buffer = app_buffer_init(BUFFER_SIZE);
    device->is_running = 0;
    device->post_read = NULL;
    device->pre_write = NULL;

    // 初始化线程池模块
    app_pool_init(POOL_SIZE);

    // 初始化mqtt模块
    app_mqtt_init();

    return device;
}


// 在线程池中某个分线程执行的任务函数
static int send_task_fun(void *arg) {
    // 从上行缓冲区读取消息数据
    char data_buf[128];
    int len = app_buffer_read(device->up_buffer, data_buf, sizeof(data_buf));
    if (len <=0) {
        log_error("读取上行缓冲区数据失败");
        return -1;
    }
    // 将字符数组数据转换为json消息
    char *json = app_message_charsToJson(data_buf, len);

    // 将json消息发送到远程
    int result = app_mqtt_send(json);
    if (result == -1) {
        log_error("发送消息失败");
        free(json);
        return -1;
    }

    log_debug("发送消息成功: %s", json);
    free(json);
    
    return 0;
}

// 不断读取串口数据的线程函数
static void *read_thread_fun(void *arg) {
    while(device->is_running) {
        // 读取串口数据
        char data_buf[128];
        int len = read(device->fd, data_buf, sizeof(data_buf));
        // log_debug("读取数据长度: %d", len);
        if (len > 0 && device->post_read) {
            // 将读取的数据处理成字符数组消息
            len = device->post_read(data_buf, len);
            
        }
        if (len > 0) {
            // 将字符数组消息保存到上行缓冲区
            app_buffer_write(device->up_buffer, data_buf, len);
            // 注意任务：将发送消息到远程的操作交给线程池
            app_pool_registerTask(send_task_fun, NULL);
        }
    }
}

// 由线程池中的某个分线程调用
static int write_task_fun(void *arg) {
    // 从下行缓冲区读取字符数组消息
    char data_buf[128];
    int len = app_buffer_read(device->down_buffer, data_buf, sizeof(data_buf));
    if (len <=0) {
        log_error("读取下行缓冲区数据失败");
        return -1;
    }
    // 将字符数组消息转换为传输格式数据
    if (device->pre_write) {
        len = device->pre_write(data_buf, len);
    }

    if (len > 0) {

        // 如果没有达到200ms的时间间隔限制， 需要等待相差时间 
        long interval_time = app_common_getCurrentTime() - device->last_write_time;
        if (interval_time < INTERVAL_TIME_MS) {
            usleep(INTERVAL_TIME_MS-interval_time);
        }

        // 将传输格式数据写入串口，发送下游设备
        int write_len = write(device->fd, data_buf, len);
        if (write_len != len) {
            log_error("写入串口数据失败");
            return -1;
        }
        // 保存写入的时间 
        device->last_write_time = app_common_getCurrentTime();
        log_debug("写入串口发送数据成功: %s", app_common_charsToHexstr(data_buf, len));
        return 0;
    }

    return -1;
}

// 接收处理远程消息的回调函数
static int receive_msg_callback(char *json) {
    // 将json消息转换为字符数组消息
    char data_buf[128];
    int len = app_message_jsonToChars(json, data_buf, sizeof(data_buf));
    // 将字符数据消息写入下行缓冲区
    app_buffer_write(device->down_buffer, data_buf, len);
    // 注册任务： 将后续的操作的线程池中的分线程处理
    app_pool_registerTask(write_task_fun, NULL);
}


int app_device_start(void)
{
    if (device->is_running) {
        log_debug("设备已经正在运行中。。。");
        return 0;
    }
    device->is_running = 1;
    // 创建读线程
    pthread_create(&device->read_thread, NULL, read_thread_fun, NULL);

    // 向mqtt模块注册一个接收处理远程消息的回调函数
    app_mqtt_registerRecvCallback(receive_msg_callback);

    return 0;
}

void app_device_destroy(void)
{
    close(device->fd);
    app_buffer_free(device->up_buffer);
    app_buffer_free(device->down_buffer);
    pthread_cancel(device->read_thread);
    device->is_running = 0;
    device->post_read = NULL;
    device->pre_write = NULL;
    app_pool_destroy();
    app_mqtt_close();
}
